classdef DataViewer < handle
    properties
        nmrg
        job
        gui
        guiData
        dataPackage
        
        extData
        isExternalData = false;
        
        current_selection_job
        current_selection_index
        selectionParamData
        selectionResultsData
    end
    
    methods
        function obj = DataViewer(nmrg, datafile)
            obj.nmrg = nmrg;
            obj.job = Table1.NMRGJobs.GetRFSpectrum(obj.nmrg);
            
            obj.makeGUI();
            
            if nargin == 1
                obj.link2Data(); % defaut: load interal data
            elseif nargin == 2
                try
                    [p, f, file_ext] = fileparts(datafile);
                    if isempty(p)
                        obj.extData.path = ['.', Constant.PathDelimiter];
                    else
                        obj.extData.path = [p,  Constant.PathDelimiter];
                    end
                    obj.extData.file = [f, file_ext];
                    
                    matFile = load([obj.extData.path, obj.extData.file]);
                    matFileTags = fields(matFile);
                    data = matFile.(matFileTags{1}).measurement_data;
                    obj.link2Data(data);
                catch
                    obj.link2Data();
                    error('Cannot link to [external] data. Link to [internal] data instead.');
                end
            else
                error('Wrong input.');            
            end
        end
        
        %%
        function delete(obj)
            L = log4m.getLogger;
            delete(obj.gui);
            L.info('DataViewer::delete', 'DataViewer deleted.');
        end
        
        function gui = makeGUI(obj)
            gui = Miscellaneous.GUICreator.GUICreator('Name', 'NMRG Data Viewer [No Data]');
            menuData = uimenu(gui.figureHandle, 'Text','Data');
            menuSave = uimenu(gui.figureHandle, 'Text','Save');
            hdls.menuDataInt = uimenu(menuData,'Text','Link to Internal Data', 'MenuSelectedFcn', @(src, event) obj.link2Data(), 'Checked', 'on');
            hdls.menuDataExt = uimenu(menuData,'Text','Link to External Data', 'MenuSelectedFcn', @(src, event) obj.loadExtData(), 'Checked', 'off' );
            hdls.menuSaveSess = uimenu(menuSave,'Text','Show Buffer', 'MenuSelectedFcn', @(src, event) obj.showSessBuff() );

            % Global Grid
            grd = gui.make_grid([], [2, 3], 'RowHeight',   {'1x', '1x'}, 'ColumnWidth', {'1x', '2x', '5x'});
            
            % subgrid 1: Panel: Data Select
            subgrd1 = grd.subGrid_in_Panel([1 2], 1, [3 3], 'Data Select', ...
                                          'RowHeight', {'1x', '3x', 20},  'ColumnWidth', {'1x', '1x', '1x'}, 'Padding', [0 10 0 0]);
            hdls.listboxTag  = subgrd1.placeUI('uilistbox',   1, [1 3], 'ValueChangedFcn', @(src, event) obj.tagSelectChanged(src, event) );
            hdls.listboxData = subgrd1.placeUI('uilistbox',   2, [1 3], 'ValueChangedFcn', @(src, event) obj.dataSelectChanged(src, event), 'Multiselect', true);
            hdls.buttonUpdate     = subgrd1.placeUI('uibutton', 3, 1, 'Text', 'Update', 'ButtonPushedFcn', @(btn,event) obj.updateButtonPushed(btn, event) );
            hdls.buttonMark       = subgrd1.placeUI('uibutton', 3, 2, 'Text', 'Mark',   'ButtonPushedFcn', @(btn,event) obj.markButtonPushed(btn, event) );
            hdls.listboxTag.Items = {'GetRFSpectrum', 'GetRFMagnetometerSensitivity', 'GetWorkingPoint', 'GetMagnetometerSensitivity', 'GetMagnetometerBandwidth', 'GetXe129FID', 'GetXe131FID', 'GetXe129T1Rho', 'GetXe131T1Rho'};
            
            % Parameters & Results
            subgrd2 = grd.subGrid_in_Panel(1, 2, [2 3], 'Parameters', ...
                                          'RowHeight', {'1x', 20},  'ColumnWidth', {'1x', '1x', '1x'}, 'Padding', [0 10 0 0]);
            hdls.tableParam = subgrd2.placeUI('uitable', 1, [1 3], 'CellSelectionCallback', @(src, event) obj.paramTableSelection(src, event) );
            hdls.buttonPlotParam = subgrd2.placeUI('uibutton', 2, 2, 'Text', 'Plot', 'Enable', 'off', 'ButtonPushedFcn', @(btn, event) obj.plot_param_data(btn, event) );
            
            subgrd3 = grd.subGrid_in_Panel(2, 2, [2 3], 'Results', ...
                                          'RowHeight', {'1x', 20},  'ColumnWidth', {'1x', '1x', '1x'}, 'Padding', [0 10 0 0]);
            hdls.tableRes   = subgrd3.placeUI('uitable', 1, [1 3], 'CellSelectionCallback', @(src, event) obj.resultTableSelection(src, event));
            hdls.buttonPlotRes = subgrd3.placeUI('uibutton', 2, 2, 'Text', 'Plot', 'Enable', 'off', 'ButtonPushedFcn', @(btn, event) obj.plot_result_data(btn, event) );
            
            % Panel : Plot
            hdls.panelPlot = grd.placeUI('uipanel', [1 2], 3, 'Title', 'Plots', 'AutoResizeChildren', 'off');
            
            gui.uiHandle = hdls;
            obj.gui = gui;
            
            obj.current_selection_job = 'GetRFSpectrum';
            obj.current_selection_index = 1;
        end
        
        %%
        function show(obj)
            if isempty(obj.gui)
                obj.makeGUI().show();
                return;
            end
            
            if ~isvalid(obj.gui.figureHandle)
                obj.makeGUI().show();
                return
            end
            
            if ~obj.gui.isShown()
                obj.gui.show();
            end
        end
        
        function hide(obj)
            obj.gui.hide();
        end
        
        function close(obj)
            L = log4m.getLogger;
            delete(obj.gui);
            L.info('DataViewer::close', 'DataViewer deleted.');
        end
               
        %%
        function obj = link2Data(obj, guiData)
            L = log4m.getLogger;
            if nargin == 1
                getSession;
                obj.guiData = sess.getBufferData();
                obj.isExternalData = false;
                obj.gui.figureHandle.Name = sprintf('NMRG Data Viewer: [%s]', 'Internal');
                obj.gui.uiHandle.buttonMark.Enable = 'on';
                obj.gui.uiHandle.menuDataInt.Checked = 'on';
                obj.gui.uiHandle.menuDataExt.Checked = 'off';
                L.info('DataViewer::link2Data', 'Link to Internal Session data.');
            else
                obj.guiData = guiData;
                obj.isExternalData = true;
                obj.gui.figureHandle.Name = sprintf('NMRG Data Viewer: [%s]', obj.extData.file);
                obj.gui.uiHandle.buttonMark.Enable = 'off';
                obj.gui.uiHandle.menuDataInt.Checked = 'off';
                obj.gui.uiHandle.menuDataExt.Checked = 'on';
                L.info('DataViewer::link2Data', sprintf('Link to External Session data [%s].', obj.extData.file) );
            end
            
            obj.dataSelectListUpdate(obj.current_selection_job);
        end
        
        function data = loadExtData(obj)
            [f, p] = uigetfile(Constant.AutoMeasurementDataPath, '*.mat');
            if f ~= 0
                try
                    obj.extData.file =f; obj.extData.path = p;
                    matFile = load([obj.extData.path, obj.extData.file]);
                    matFileTags = fields(matFile);
                    data = matFile.(matFileTags{1}).measurement_data;
                    obj.link2Data(data);
                catch
                    errordlg('File loading failed.');
                end
            end
        end
        
        %%
        function tagSelectChanged(obj, src, ~)
            try 
                obj.job = Table1.NMRGJobs.(src.Value)(obj.nmrg);
                obj.current_selection_job = src.Value;
                obj.current_selection_index = 1;
            catch
                error('Cannot create an NMRGJob with name [%s]', src.Value);
            end
            obj.dataSelectListUpdate(src.Value);
        end
        
        function dataSelectChanged(obj, src, ~)
            idx = cell2mat(cellfun(@(v) find(strcmp(v, src.Items)), src.Value, 'UniformOutput', false));
            obj.updatePresentation(obj.job.name, idx);
            obj.current_selection_index = idx;

            if ~obj.isExternalData
                getSession;
                data=sess.getBufferData(obj.job.name);
                isAllMarked = all([data(idx).status]);
                
                if isAllMarked
                    obj.gui.uiHandle.buttonMark.Text = 'UnMark';
                else
                    obj.gui.uiHandle.buttonMark.Text = 'Mark';
                end
            end
            
            obj.gui.uiHandle.buttonPlotParam.Enable = 'off';
            obj.gui.uiHandle.buttonPlotRes.Enable = 'off';
        end
        
        function updateButtonPushed(obj, ~, ~)
            if obj.isExternalData
                matFile = load([obj.extData.path, obj.extData.file]);
                matFileTags = fields(matFile);
                data = matFile.(matFileTags{1}).measurement_data;
                obj.link2Data(data);
            else
                obj.link2Data();
            end
            jobName = obj.gui.uiHandle.listboxTag.Value;
            obj.dataSelectListUpdate(jobName);
        end
        
        function markButtonPushed(obj, src, ~)
            idx = obj.current_selection_index;            
            getSession;
            if strcmp(src.Text , 'Mark')
                arrayfun(@(k) sess.mark(obj.job.name, 'index', k), idx);
                obj.link2Data();
                src.Text = 'UnMark';
            else
                arrayfun(@(k) sess.unMark(obj.job.name, 'index', k), idx);
                obj.link2Data();
                src.Text = 'Mark';
            end
        end
        
        %%
        function obj = dataSelectListUpdate(obj, jobName)
            try
                dataList = obj.guiData.(jobName);
                items = obj.make_items(dataList);
            catch
                items = {};
            end
            obj.gui.uiHandle.listboxData.Items = items;

            if max(obj.current_selection_index) <= length(items)
                obj.gui.uiHandle.listboxData.Value = items(obj.current_selection_index);
                obj.updatePresentation(obj.job.name, obj.current_selection_index);
            end
        end
        
        function updatePresentation(obj, jobname, idx)
            obj.gui.uiHandle.listboxData.Enable = 'off';
            obj.gui.uiHandle.buttonUpdate.Enable = 'off';
            waitfor(obj.gui.uiHandle.listboxData, 'Enable', 'off');
            if length(idx) == 1
                obj.job.dataPackage = obj.guiData.(jobname)(idx).data;
                obj.job.presentation(obj);
            else
                jobList = obj.make_jobList(jobname, idx);
                Table1.NMRGJobs.(jobname).compare(jobList, 'viewer', obj);
            end
            obj.gui.uiHandle.listboxData.Enable = 'on';
            obj.gui.uiHandle.buttonUpdate.Enable = 'on';
        end
        
        function paramTableSelection(obj, src, event)
            sel_data = event.Indices;
            try
                values = cell2mat(arrayfun(@(r, c) src.Data{r, c}, sel_data(:, 1), sel_data(:, 2), 'UniformOutput', false));
                if isnumeric(values)
                    obj.gui.uiHandle.buttonPlotParam.Enable = 'on';
                    obj.selectionParamData = values;
                else
                    obj.gui.uiHandle.buttonPlotParam.Enable = 'off';
                    obj.selectionParamData = [];
                end
            catch
                obj.gui.uiHandle.buttonPlotParam.Enable = 'off';
                obj.selectionParamData = [];
            end
        end
        function plot_param_data(obj, ~, ~)
            f=figure; ax=axes('Parent', f);
            plot(ax, obj.selectionParamData, 'o-'); grid(ax, 'on');
        end
        
        
        function resultTableSelection(obj, src, event)
            sel_data = event.Indices;
            try
                values = cell2mat(arrayfun(@(r, c) src.Data{r, c}, sel_data(:, 1), sel_data(:, 2), 'UniformOutput', false));
                if isnumeric(values)
                    obj.gui.uiHandle.buttonPlotRes.Enable = 'on';
                    obj.selectionResultsData = values;
                else
                    obj.gui.uiHandle.buttonPlotRes.Enable = 'off';
                    obj.selectionResultsData = [];
                end
            catch
                obj.gui.uiHandle.buttonPlotRes.Enable = 'off';
                obj.selectionResultsData = [];
            end
        end
        function plot_result_data(obj, ~, ~)
            f=figure; ax=axes('Parent', f);
            plot(ax, obj.selectionResultsData, 'o-'); grid(ax, 'on');
        end
        
        function showSessBuff(~)
            getSession;
            sess.showGUI;
        end
    end
    
    methods (Access = private)
        function jobList = make_jobList(obj, jobname, idx)
            dataList = obj.guiData.(jobname)(idx);
            jobList = arrayfun(@(~) Table1.NMRGJobs.(jobname)(obj.nmrg), 1:length(dataList));
            for k = 1:length(dataList)
                jobList(k).dataPackage = dataList(k).data;
            end
        end
        
        function items = make_items(~, dataList)
            timestampList = arrayfun(@(d) datestr(d.timestamp),  dataList,  'UniformOutput',  false);
            statusList = arrayfun(@(d) d.status,  dataList);
            for k=1:length(statusList)
                if statusList(k)
                    timestampList{k} = sprintf('[%03d] %s *', k, timestampList{k});
                else
                    timestampList{k} = sprintf('[%03d] %s', k, timestampList{k});
                end
            end
            items = timestampList;
        end
    end
end

